package com.bandi.tharshesnows.parser;

import java.io.IOException;
import java.net.URL;
import java.text.MessageFormat;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.htmlcleaner.HtmlCleaner;
import org.htmlcleaner.TagNode;

import android.util.Log;

import com.bandi.tharshesnows.GetDataCallback;
import com.bandi.tharshesnows.geolocating.GeocoderService;
import com.bandi.tharshesnows.model.County;
import com.bandi.tharshesnows.model.Location;
import com.bandi.tharshesnows.model.Resort;
import com.bandi.tharshesnows.model.Skirun;
import com.bandi.tharshesnows.model.SkirunStatus;
import com.bandi.tharshesnows.pics.PictureStorage;

public class RomaniaTuristicaParser2 {

  private static final String TAG = "SNOW";
  private static final MessageFormat AVG_SNOW_HEIGHT_FORMAT = new MessageFormat("Strat mediu zapada: {0} cm");
  private static final MessageFormat COUNTY_NAME_FORMAT = new MessageFormat("mix {0}");

  private String url;
  private String rootUrl;// "http://www.romaniaturistica.ro"
  private ExecutorService executorService = Executors.newFixedThreadPool(5);
  private static RomaniaTuristicaParser2 INSTANCE;

  private RomaniaTuristicaParser2(String url, String rootUrl) {
    this.url = url;
    this.rootUrl = rootUrl;
  }

  public static void init(String url, String rootUrl) {
    if (INSTANCE == null) {
      INSTANCE = new RomaniaTuristicaParser2(url, rootUrl);
    }
  }

  public static RomaniaTuristicaParser2 getParser() {
    if (INSTANCE == null) {
      throw new IllegalStateException("RomaniaTuristicaParser has not been initialized.");
    }
    return INSTANCE;
  }

  public static void destroy() {
    if (INSTANCE != null) {
      INSTANCE.executorService.shutdownNow();
    }
  }

  public void parseAllCounties(final GetDataCallback<ArrayList<County>> callback) {
    executorService.execute(new Runnable() {

      @Override
      public void run() {
        try {
          HtmlCleaner cleaner = new HtmlCleaner();
          TagNode document = cleaner.clean(new URL(url));
          List<TagNode> resortNodesList = document.getElementListByAttValue("id", "statiuni", true, true);
          if (!resortNodesList.isEmpty()) {
            TagNode resortNodes = resortNodesList.get(0);
            TagNode countiesUL = (TagNode) resortNodes.getElementListByName("ul", false).get(0);
            List<TagNode> resortLIs = countiesUL.getElementListByName("li", false);
            callback.onData(parseCountiesFromResortList(resortLIs));
          }
          else {
            Log.d(TAG, "No resorts.");
          }
          Log.d(TAG, "html cleaner finished cleaning.");
        }
        catch (Throwable e) {
          Log.e(TAG, "parsing failed", e);
          // TODO - handle error
        }
      }
    });
  }

  protected ArrayList<County> parseCountiesFromResortList(List<TagNode> resortLIs) throws ParseException,
      NumberFormatException, IOException {
    Log.d(TAG, "found " + resortLIs.size() + " resorts.");
    HashMap<String, County> counties = new HashMap<String, County>();
    for (TagNode resortLI : resortLIs) {
      String resortLIClassAttribute = resortLI.getAttributeByName("class");
      String countyName = (String) COUNTY_NAME_FORMAT.parse(resortLIClassAttribute)[0];
      if (!counties.containsKey(countyName)) {
        County county = new County();
        county.setName(countyName);
        if (county.getLocation() == null || county.getLocation().getStaticMapImagePath() == null) {
          //county.setLocation(GeocoderService.getInstance().getLocation(county));
        }
        counties.put(countyName, county);
        county.setResorts(new ArrayList<Resort>());
        county.getResorts().add(parseResortFromTagNode(county, resortLI));
      }
      else {
        County county = counties.get(countyName);
        county.getResorts().add(parseResortFromTagNode(county, resortLI));
      }
    }
    for(County county : counties.values()){
      if (county.getLocation() == null || county.getLocation().getStaticMapImagePath() == null) {
        county.setLocation(GeocoderService.getInstance().getLocation(county));
      }
    }
    Log.d(TAG, "Found " + counties.size() + " counties.");
    return new ArrayList<County>(counties.values());
  }

  private Resort parseResortFromTagNode(County county, TagNode resortLI) throws NumberFormatException, ParseException,
      IOException {
    // get container for resort details and skirun names
    TagNode div = resortLI.getChildTags()[0];
    // get resort details
    TagNode resortDiv = div.getChildTags()[0];
    TagNode a = resortDiv.getChildTags()[0];
    TagNode[] spans = a.getElementsByName("span", true);
    Resort resort = new Resort(county);
    resort.setName(spans[0].getText().toString());
    String spanSnowHeight = spans[2].getText().toString();
    String parsedSnowHeight = (String) AVG_SNOW_HEIGHT_FORMAT.parse(spanSnowHeight)[0];
    double snowHeight = Double.parseDouble(parsedSnowHeight);
    resort.setAverageSnowHeight(snowHeight);
    resort.setDegrees(spans[3].getText().toString());
    TagNode weatherImg = spans[4].findElementByName("img", true);
    String src = weatherImg.getAttributeByName("src");
    String[] tokens = src.split("/");
    String weatherImageName = tokens[tokens.length - 1];
    PictureStorage.getInstance().storeImage(src, weatherImageName);
    resort.setWeatherImagePath(weatherImageName);
    Log.d(TAG, resort.toString());
    // get skirun details
    TagNode skirunsDiv = div.getChildTags()[1];
    TagNode[] skirunSpans = skirunsDiv.getElementsByAttValue("class", "span4 well", true, true);
    resort.setSkiruns(new ArrayList<Skirun>());
    for (TagNode skirunSpan : skirunSpans) {
      Skirun skirun = new Skirun(resort);
      TagNode link = skirunSpan.findElementByName("a", true);
      skirun.setRemoteURL(link.getAttributeByName("href"));
      skirun.setName(link.getText().toString());
      TagNode openCloseDiv = skirunSpan.findElementByName("div", true);
      skirun.setSkirunStatus(SkirunStatus.parse(openCloseDiv.getText().toString().trim()));
      Log.d(TAG, skirun.toString());
      resort.getSkiruns().add(skirun);
    }
    return resort;
  }

  public void updateSkirun(Skirun skirun, GetDataCallback<Skirun> updateCallback) {
    // TODO Auto-generated method stub

  }

  public void getNewSkirun(Skirun skirun, GetDataCallback<Skirun> updateCallback) {
    // TODO Auto-generated method stub

  }

}
